/**
 * 硬盘数据接收者
 * Copyright(C) 2018 liumurong
 */

import { Receiver } from "./receiver";
import { Readable } from "stream";
import { LocalReadStream } from "./localreadstream";
import * as fse from "fs-extra";
import * as path from "path";
import { FileSlice, MetadataOptions } from "./models";
import { MerkleTree } from "merkletreets";

/**
 * Local
 */
export class LocalReceiver extends Receiver {

    /**
     * LocalReceiver
     * @param directory 文件存放目录
     */
    constructor(directory: string) {
        super(directory);
    }

    /**
    * 保存切片
    * @param slice 文件切片
    */
    async saveSlice(slice: FileSlice) {
        // 保存文件
        await fse.ensureDir(this.target);
        let filePath = path.join(this.target, slice.hash);
        await fse.ensureDir(filePath);
        let fileName = path.join(filePath, slice.hash + "_" + slice.index + ".s")
        await fse.writeFile(fileName, slice.data);
        // 完整性验证
        let hashTree = this.merkleTrees.get(slice.hash);
        if (!hashTree) {
            hashTree = new MerkleTree({
                leafcount: slice.slicecount
            });
            this.merkleTrees.set(slice.hash, hashTree);
        }
        hashTree.updateLeaf(slice.index, slice.data);
        hashTree.update();          // 重新构造整棵树可能会导致主进程卡住，暂时先测试用用，需要优化
        // 更新元数据
        let metadataFilePath = path.join(filePath, "metadata.slice");   // 元数据文件固定名
        await fse.writeFile(metadataFilePath, JSON.stringify({
            hash: slice.hash,
            name: slice.name,
            slicecount: slice.slicecount,
            size: slice.size,
            root: hashTree.root().hash
        }));
    }
    /**
     * 文件元数据
     * @param hash 文件HASH值，采用sha256生成
     */
    async getNodeHash(hash: string, depth: string, index: string): Promise<string> {
        let hashTree = this.merkleTrees.get(hash);
        // 树存在
        if (hashTree) {
            return hashTree.getTreeNode(parseInt(depth), parseInt(index)).hash;
        }
        // 树不存在，但是slice文件存在
        let metadataFilePath = path.join(this.target, hash, "metadata.slice");
        if (await fse.pathExists(metadataFilePath)) {
            let opts = JSON.parse((await fse.readFile(metadataFilePath)).toString());
            if (depth === "0" && index === "0") {
                // 如果是获取根节点，那么直接返回root
                return opts.root;
            }
            // 构造树
            hashTree = new MerkleTree({
                leafcount: opts.slicecount
            });
            for (let i = 0; i < opts.slicecount; i++) {
                let sliceFilePath = path.join(this.target, opts.hash + "_" + i + ".s");
                if (await fse.pathExists(sliceFilePath)) {
                    let chunk = await fse.readFile(sliceFilePath);
                    hashTree.updateLeaf(i, chunk);
                }
            }
            hashTree.update();
            this.merkleTrees.set(opts.hash, hashTree);
            return hashTree.getTreeNode(parseInt(depth), parseInt(index)).hash;
        }
        // 树不存在,slice文件也不存在, 那么直接返回个值就可以了
        return "";
    }
    /**
     * 获取文件元数据
     * @param hash 文件HASH
     */
    async getMetadata(hash: string) {
        let metadataFilePath = path.join(this.target, hash, "metadata.slice");   // 元数据文件固定名
        if (!await fse.pathExists(metadataFilePath)) {
            return;
        }
        // 读取元数据
        return JSON.parse((await fse.readFile(metadataFilePath)).toString());
    }

    /**
     * 获取文件流
     * @param hash 文件MD5
     */
    getStream(hash: string): Readable {
        return new LocalReadStream(hash, this.target, this);
    }

}
